package ar.republicagroup.gestion.dao.jpa;

import ar.republicagroup.gestion.dao.ChequeDAO;
import ar.republicagroup.gestion.entities.Banco;
import ar.republicagroup.gestion.entities.Cheque;
import ar.republicagroup.gestion.entities.ChequeEstado;
import ar.republicagroup.gestion.entities.ChequeTipo;
import ar.republicagroup.gestion.entities.CuentaBanco;
import ar.republicagroup.gestion.services.ChequeEstadoServices;
import ar.republicagroup.gestion.util.AppSession;
import ar.republicagroup.gestion.util.JPAUtil;
import ar.republicagroup.gestion.util.helpers.MapSorting;
import ar.republicagroup.gestion.util.messages.UIMessages;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.apache.log4j.Logger;


public class ChequeDAOImplJPA implements ChequeDAO{
    
        private static Logger log = Logger.getLogger(ChequeDAOImplJPA.class);
	private EntityManager	em;
        
        public final int CHEQUE_INGRESADO = 1;
        public final int CHEQUE_ACTIVO    = 1;
	
	public ChequeDAOImplJPA(){
		em = JPAUtil.getEntityManager();
	}
	

	@Override
	public List<Cheque> findEntities() {
		TypedQuery<Cheque> query = em.createQuery("SELECT c FROM Cheque c WHERE c.estado = 1", Cheque.class);
		return query.getResultList();
	}
        

	@Override
	public String insert(Cheque beanCheque) {
		String resultado = null;
		try{
			em.getTransaction().begin();
			em.persist(beanCheque);
			em.getTransaction().commit();
			resultado = "Se registró el cheque correctamente";
                        log.info("Insertando cheque...");
                    }catch(Exception ex){
                        log.error("Error insertando cheque...",ex);
			em.getTransaction().rollback();
                        
                    }
		
           finally {
                        em.close();
                   }
                
                return resultado;
	}

	@Override
	public String update(Cheque beanCheque) {
                    log.info("Actualizando datos del cheque..."+beanCheque.getId());
                    String mensaje = null;
                    Cheque cheque = em.find(Cheque.class, beanCheque.getId());
                    cheque.setBancoId(beanCheque.getBancoId());
                    cheque.setBolsaChequeId(beanCheque.getBolsaChequeId());
                    cheque.setChequeEstadoId(beanCheque.getChequeEstadoId());
                    cheque.setChequeNro(beanCheque.getChequeNro());
                    cheque.setChequeTipoId(beanCheque.getChequeTipoId());
                    cheque.setCodigoCtaBancaria(beanCheque.getCodigoCtaBancaria());
                    cheque.setCodigoString(beanCheque.getCodigoString());
                    cheque.setComision(beanCheque.getComision());
                    cheque.setCuentaBancoId(beanCheque.getCuentaBancoId());
                    cheque.setEstado(beanCheque.getEstado());
                    cheque.setFechaAlta(beanCheque.getFechaAlta());
                    cheque.setFechaPendiente(beanCheque.getFechaPendiente());
                    cheque.setFechaUpdate(beanCheque.getFechaUpdate());
                    cheque.setFechaLiquidado(beanCheque.getFechaLiquidado());
                    cheque.setImporte(beanCheque.getImporte());
                    cheque.setOrigen(beanCheque.getOrigen());
                    cheque.setGestionEnRechazos(beanCheque.getGestionEnRechazos());
               
               try{    
                    em.getTransaction().begin();
                    em.merge(cheque);
                    em.getTransaction().commit();
                    mensaje = UIMessages.getInfoMessage("onCompleteUpdate");
                    log.info(UIMessages.getInfoMessage("onCompleteUpdate"));
                }catch(Exception ex){
                    em.getTransaction().rollback();
                    log.error("Error al actualizar cheque...",ex);
                }
                
                return mensaje;
	}

	@Override
	public Cheque get(Integer id_cheque) {
		return em.find(Cheque.class, id_cheque);
	}
        
        @Override
        public List<Cheque> getChequesByNumeroDeCheque(String numeroDeCheque) {
                if(!em.isOpen()) { em = JPAUtil.getEntityManager(); }
                
                try{
                    TypedQuery<Cheque> query = em.createQuery("SELECT c FROM Cheque c WHERE c.chequeNro LIKE ?1 AND c.estado = 1", Cheque.class);
                    query.setParameter(1, "%"+numeroDeCheque+"%");
                    return query.getResultList();
                }catch(Exception ex){
                    log.error("Error en getChequesByNumeroDeCheque()",ex);
                    return null;
                }
                
        }

        @Override
        public List<Cheque> getChequesByImporte(double importe) {
            
             if(!em.isOpen()) { em = JPAUtil.getEntityManager(); }
                try{
                    TypedQuery<Cheque> query = em.createQuery("SELECT c FROM Cheque c WHERE c.importe = ?1 AND c.estado = 1", Cheque.class);
                    query.setParameter(1, importe);
                    return query.getResultList();
                }catch(Exception ex){
                    log.error("Error en getChequesByImporte()",ex);
                    return null;
                }
        }
        
        
        @Override
        public boolean isChequeRegistered(String codigoStringDeCheque) {
            boolean isRegistered = false;   
            
            if(!em.isOpen()) { em = JPAUtil.getEntityManager(); }
            
                    TypedQuery<Cheque> query = em.createQuery("SELECT c FROM Cheque c WHERE c.codigoString LIKE ?1 AND c.estado = 1", Cheque.class);
                    query.setParameter(1, codigoStringDeCheque);
                
                if(!query.getResultList().isEmpty()){
                    log.info("Se encontró un cheque duplicado : "+codigoStringDeCheque);
                    isRegistered = true;
                }
                
                if(!isRegistered){
                     String cod_cuenta_banco		= codigoStringDeCheque.substring(0, 3);
                     String nro_cheque                  = codigoStringDeCheque.substring(10, 18);
                     String cuenta_bancaria		= codigoStringDeCheque.substring(18);
                     
                     TypedQuery<Cheque> query2 = em.createQuery("SELECT c FROM Cheque c WHERE c.bancoId.codigoCuenta = ?1 AND c.chequeNro = ?2 AND c.codigoCtaBancaria = ?3 AND c.estado = 1", Cheque.class);
                     query2.setParameter(1, "00"+cod_cuenta_banco);
                     query2.setParameter(2, nro_cheque);
                     query2.setParameter(3, cuenta_bancaria);
                     
                     if(!query2.getResultList().isEmpty()){
                         log.info("Se encontró un cheque con igual banco, nro cheque y cuenta : "+codigoStringDeCheque);
                         isRegistered = true;
                     }
                }
                
                return isRegistered;
         }
        
    

	@Override
	public String delete(Integer id_cheque) {
                
                if(!em.isOpen()) { em = JPAUtil.getEntityManager(); }
            
                String resultado                = null;
		Cheque deleteCheque 		= em.find(Cheque.class, id_cheque);
                deleteCheque.setCodigoString(deleteCheque.getCodigoString()+"E");   //Le agregamos una 'E' para que vuelva a estar disponible el código
		deleteCheque.setEstado(0);      //<--- Eliminación lógica , estado = 0
		
                try{
                    em.getTransaction().begin();
                    em.merge(deleteCheque);	
                    em.getTransaction().commit();
                    resultado = "Se eliminó el cheque #"+deleteCheque.getChequeNro();
                    log.info("Eliminando cheque..."+deleteCheque.getChequeNro());
                    }catch(Exception ex){
                     log.error(ex.getMessage(),ex);
				em.getTransaction().rollback();
                    }
                
                   finally{
                       em.close();
                     }
                            
                    return resultado;
        }

    @Override
    public List<Cheque> getChequesByBolsaId(int bolsa_id) {
        log.info("Obteniendo cheques por bolsa id... "+bolsa_id);
        if(!em.isOpen()) { em = JPAUtil.getEntityManager(); }
            
                TypedQuery<Cheque> query = em.createQuery("SELECT c FROM Cheque c WHERE c.bolsaChequeId.id = ?1 AND c.estado = 1", Cheque.class);
		query.setParameter(1, bolsa_id);
                return query.getResultList();
    }

    @Override
    public Map<String, Object> getTotalesProvCap() {
        
        if(!em.isOpen()){ em = JPAUtil.getEntityManager();}
            log.info("Obteniendo TotalesProvCap - > getTotalesProvCap()");
            Date hoy_inicio = new Date();
            hoy_inicio.setHours(0);
            hoy_inicio.setMinutes(0);
            hoy_inicio.setSeconds(0);
            
            Date hoy_fin = new Date();
            hoy_fin.setHours(23);
            hoy_fin.setMonth(59);
            hoy_fin.setSeconds(59);
        
            Query query = em.createQuery("SELECT c.origen,SUM(c.importe) AS importe FROM Cheque c WHERE c.fechaAlta BETWEEN ?1 AND ?2 "
                    + "OR (c.fechaUpdate BETWEEN ?1 AND ?2 AND c.chequeEstadoId.id = "+ChequeEstadoServices.CHEQUE_INGRESADO+")"
                    + "AND c.estado = "+ CHEQUE_ACTIVO + " GROUP BY c.origen ORDER BY c.origen");
            query.setParameter(1, hoy_inicio);
            query.setParameter(2, hoy_fin);
            
            List<Object[]> results = query.getResultList();
            Map<String,Object> totalOrigenMap = new HashMap<>();
            
            log.info(results.size());
            
            for(Object[] row : results){
               totalOrigenMap.put(row[0].toString(), row[1]);
            }
            
            return totalOrigenMap;
        
    }

    @Override
    public Map<String, Object> getLiquidacionesPosiblesPorCadaCliente(Calendar fechaLiquidacion) {
        log.info("Obteniendo... getLiquidacionesPosiblesPorCadaCliente()");
        
        if(!em.isOpen()){ em = JPAUtil.getEntityManager();}
        
            Date fechita = fechaLiquidacion.getTime();
            fechita.setHours(23);
            fechita.setMinutes(59);
            fechita.setSeconds(59);
            
            
            String myJpqlQuery = "SELECT c.bolsaChequeId.clienteId,SUM((c.importe - (c.importe * c.comision/100))) "
                    + "FROM Cheque c WHERE c.estado = :chequeActivo "
                    + "AND (c.fechaAlta <= :fechaLiquidacion) "
                    + "AND (c.chequeEstadoId.id IN (1,3,4,5)) ";
                    
                    if(!esProximaLiquidacion(fechaLiquidacion)){
                        log.info("--> Liquidaciones posibles del día");
                        myJpqlQuery +="AND (c.fechaUpdate IS NULL OR (CAST(c.fechaUpdate DATE) <= CURRENT_DATE)) ";
                        }
                   myJpqlQuery += " AND c.fechaLiquidado IS NULL";
                   myJpqlQuery += " GROUP BY c.bolsaChequeId.clienteId.id ";
                    
            Query query = em.createQuery(myJpqlQuery);
            query.setParameter("chequeActivo", CHEQUE_ACTIVO);
            query.setParameter("fechaLiquidacion", fechita);
            
            List<Object[]> results                  = query.getResultList();
            Map<String,Object> clienteImporteMap    = new HashMap<>();
            
            log.info("Liquidaciones posibles encontradas : "+results.size());
            
            for(Object[] row : results){
                clienteImporteMap.put(row[0].toString(), row[1]);
            }
            
            /* SUMA DE COMISIÓN DE RECHAZO Y PENALIZACIÓN POR CLIENTE */
            String mysqlQuery = "SELECT cli.nombre,"
            +"ROUND(SUM((c.importe)+pen.comision),2) as rechazos "
            +"FROM cheque c "
            +"INNER JOIN bolsa_cheque bc "
            +"ON c.bolsa_cheque_id = bc.id "
            +"INNER JOIN cliente cli "
            +"ON cli.id = bc.cliente_id "
            +"INNER JOIN cheque_estado ce "
            +"ON c.cheque_estado_id = ce.id "
            +"INNER JOIN cheque_penalizacion pen "
            +"ON ce.id = pen.cheque_estado_id "
            +"WHERE c.estado = 1 "
            +"AND (c.fecha_alta <= ?) "
            +"AND (c.cheque_estado_id  in(3,4,5)) "
            +"AND c.fecha_liquidado IS NULL "
            +"GROUP BY cli.nombre; ";
            
            Query queryRechazos                 = em.createNativeQuery(mysqlQuery);
            queryRechazos.setParameter(1, fechita);
            List<Object[]> resultQueryRechazos  = queryRechazos.getResultList();
            Map<String,Object> clienteRechazosMap   = new HashMap<>();
            
            for(Object[] row : resultQueryRechazos){
                clienteRechazosMap.put(row[0].toString(), row[1]);
            }
            
            log.info("Restando la comisión de rechazo y el importe por rechazo a las liquidaciones posibles...");
            for (Map.Entry<String, Object> entry : clienteRechazosMap.entrySet()){
                /* Si hay rechazos para Clientes que tienen liquidaciones posibles del día, 
                 *  actualizamos el monto de liquidación posible restándole los rechazos */
                 if(clienteImporteMap.get(entry.getKey().toString()) != null){
                     double totalLiqPosible            = Double.parseDouble(clienteImporteMap.get(entry.getKey()).toString());
                     double totalLiqPosibleMenosRechazos = totalLiqPosible - Double.parseDouble(entry.getValue().toString());
                     log.info("totalLiqPosible: "+totalLiqPosible);
                     log.info("totalLiqPosibleMenosRechazos: "+totalLiqPosibleMenosRechazos);
                     clienteImporteMap.put(entry.getKey(), totalLiqPosibleMenosRechazos);
                 }  else{
                     /*Sino se agrega como una liquidación posible nueva */
                     clienteImporteMap.put(entry.getKey(), Double.parseDouble(entry.getValue().toString()));
                 }   
             }
            
            Map clienteImporteMapSorted = MapSorting.sortByKeys(clienteImporteMap);
            return clienteImporteMapSorted;
    }
    
    
    private boolean esProximaLiquidacion(Calendar fechaLiquidacion){
        
        long milliseconds1 = fechaLiquidacion.getTimeInMillis();
        long milliseconds2 = AppSession.getFechaDeLiquidacion().getTimeInMillis();
        long diff = milliseconds1 - milliseconds2  ;
        long diffDays = diff / (24 * 60 * 60 * 1000);
        
        if(diffDays != 0){
            return true;
        }else{
            return false;
        }
        
    }
    

    @Override
    public List<Cheque> getChequesByFilters(ChequeTipo chequeTipo, ChequeEstado chequeEstado,
                                            CuentaBanco cuentaBanco,Banco banco,String origen,
                                            Calendar fechaDesde,Calendar fechaHasta) {
            
            CriteriaBuilder qb              = em.getCriteriaBuilder();
            CriteriaQuery cq                = qb.createQuery();
            Root<Cheque> cheque             = cq.from(Cheque.class);
            List<Predicate> predicates      = new ArrayList<>();
            
            
            if(chequeTipo  != null){
                predicates.add(qb.equal(cheque.get("chequeTipoId"), chequeTipo));
            }
            
            if(chequeEstado != null){
                predicates.add(qb.equal(cheque.get("chequeEstadoId"), chequeEstado));
            }
            
            if(cuentaBanco  != null){
                predicates.add(qb.equal(cheque.get("cuentaBancoId"), cuentaBanco));
            }
            
            if(banco != null){
                predicates.add(qb.equal(cheque.get("bancoId"), banco));
            }
            
            if(origen != null){
                predicates.add(qb.equal(cheque.get("origen"), origen));
            }
            
            if(fechaDesde != null && fechaHasta != null){
                Date fechaInicio = fechaDesde.getTime();
                fechaInicio.setHours(0);
                fechaInicio.setMinutes(0);
                fechaInicio.setSeconds(0);
                
                Date fechaFin    = fechaHasta.getTime();
                fechaFin.setHours(23);
                fechaFin.setMinutes(59);
                fechaFin.setSeconds(59);
                predicates.add(qb.between(cheque.get("fechaAlta").as(Date.class), fechaInicio, fechaFin));
            }
            
            
            predicates.add(qb.equal(cheque.get("estado"), 1));
            cq.select(cheque).where(predicates.toArray(new Predicate[]{}));
            cq.orderBy(qb.desc(cheque.get("fechaAlta").as(Date.class)));
            List<Cheque> resultList = em.createQuery(cq).getResultList();
            return resultList;
    }

    @Override
    public List<Object[]> getSumaDeChequesPorClienteDelDia(String fecha,String origenCheques) {
        
        log.info("Reporte...getSumaDeChequesPorClienteDelDia - Fecha : "+fecha + " "+" Origen: "+origenCheques);
        
        if(!em.isOpen()){ em = JPAUtil.getEntityManager(); }
        
        String mysqlQuery = "SELECT cli.nombre,SUM(c.importe) from bolsa_cheque bc "
                + " INNER JOIN cheque c "
                + " ON bc.id = c.bolsa_cheque_id "
                + " INNER JOIN cliente cli "
                + " ON bc.cliente_id = cli.id "
                + " WHERE CONVERT(bc.fecha_alta,date) LIKE ? "
                + " AND c.origen LIKE ? "
                + " GROUP BY cli.nombre;";
        
        log.info("Query getSumaDeChequesPorClienteDelDia : " +mysqlQuery);
        
        Query query  = em.createNativeQuery(mysqlQuery);
        query.setParameter(1, fecha);
        query.setParameter(2, origenCheques);
        List<Object[]> resultQuery  = query.getResultList();
        
        return resultQuery;
        
        
    }

    




}
